﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;

namespace Look_Through_Windows.Logic
{
    class Notifier
    {
        /// <summary>
        /// Singleton
        /// </summary>
        public static Notifier Instance { get; private set; }

        public Notifier( )
        {
            if ( Instance != null ) throw new InvalidOperationException( );
            Instance = this;
        }

        /// <summary>
        /// Duration of notification in seconds
        /// </summary>
        public double NotificationDuration = 8;
        /// <summary>
        /// Notification delay
        /// </summary>
        public double NotificationDelay = .5;

        /// <summary>
        /// Width of a notification
        /// </summary>
        public double NotificationWidth = 350;
        /// <summary>
        /// Height of a notification
        /// </summary>
        public double NotificationHeight = 70;
        /// <summary>
        /// Padding of a notification
        /// </summary>
        public double NotificationPadding = 8;

        /// <summary>
        /// Queue of notifications
        /// </summary>
        private readonly Queue<KeyValuePair<DateTime, Window>> notifications = new Queue<KeyValuePair<DateTime, Window>>( );

        /// <summary>
        /// Invokes
        /// </summary>
        private readonly Queue<Action> invokes = new Queue<Action>( );

        /// <summary>
        /// Last notification
        /// </summary>
        private DateTime lastNotificationStart = new DateTime( 0L );

        /// <summary>
        /// Update pausing
        /// </summary>
        private int pauseUpdate = 0;

        /// <summary>
        /// Adds a notification
        /// </summary>
        public void AddNotification( object content, Action onClick = null )
        {
            lock ( invokes )
            {
                invokes.Enqueue( ( ) =>
                {
                    Window window = new Window( );
                    window.Width = NotificationWidth;
                    window.Height = NotificationHeight;
                    window.WindowState = WindowState.Normal;
                    window.WindowStyle = WindowStyle.None;
                    window.WindowStartupLocation = WindowStartupLocation.Manual;
                    window.AllowsTransparency = true;
                    window.Opacity = 0;
                    window.Background = Brushes.Transparent;

                    Border border = new Border( );
                    border.Background = onClick == null ? Brushes.Orange : Brushes.LightGreen;
                    border.BorderBrush = Brushes.Black;
                    border.BorderThickness = new Thickness( 3 );
                    border.CornerRadius = new CornerRadius( 16 );

                    Label label = new Label( );
                    label.Content = content;
                    label.HorizontalAlignment = HorizontalAlignment.Center;
                    label.VerticalAlignment = VerticalAlignment.Center;
                    label.VerticalContentAlignment = VerticalAlignment.Center;
                    label.HorizontalContentAlignment = HorizontalAlignment.Center;

                    window.Content = border;
                    border.Child = label;

                    if ( onClick != null )
                        window.MouseDoubleClick += ( sender, args ) => onClick( );

                    window.MouseEnter += ( sender, args ) => pauseUpdate += 1;
                    window.MouseLeave += ( sender, args ) => pauseUpdate -= 1;
                    window.Topmost = true;
                    window.ShowInTaskbar = false;

                    DateTime starttime = DateTime.Now;
                    if ( ( starttime - lastNotificationStart ).TotalSeconds < NotificationDelay )
                        starttime = lastNotificationStart + TimeSpan.FromSeconds( NotificationDelay );

                    notifications.Enqueue( new KeyValuePair<DateTime, Window>( starttime, window ) );

                    lastNotificationStart = starttime;
                } );
            }
        }

        /// <summary>
        /// Updates the notifications
        /// </summary>
        public void Update( )
        {
            lock ( invokes )
                while ( invokes.Count > 0 )
                    invokes.Dequeue( )( );

            if ( pauseUpdate > 0 ) return;

            if ( notifications.Count == 0 ) return;

            DateTime now = DateTime.Now;

            while ( notifications.Count > 0 && ( now - notifications.Peek( ).Key ).TotalSeconds > NotificationDuration )
            {
                var kvp = notifications.Dequeue( );
                kvp.Value.Close( );
            }

            double x = System.Windows.SystemParameters.FullPrimaryScreenWidth - NotificationWidth - NotificationPadding;
            double y = System.Windows.SystemParameters.FullPrimaryScreenHeight - NotificationHeight + 22 - NotificationPadding;
            foreach ( var kvp in notifications )
            {
                double disFromStart = ( now - kvp.Key ).TotalSeconds;
                double disFromEnd = NotificationDuration - ( now - kvp.Key ).TotalSeconds;
                if ( disFromStart < 0 ) continue;

                var window = kvp.Value;
                window.Left = x;
                window.Top = y;
                y -= NotificationHeight + NotificationPadding;

                if ( !window.IsVisible )
                    window.Show( );

                double opacity = 1;
                opacity = Math.Min( disFromEnd, opacity );
                opacity = Math.Min( disFromStart, opacity );
                window.Opacity = opacity;
            }
        }
    }
}
